﻿#region " LICENSE "
// DRILL - Dependency Resolution Interface Layer Library
// Copyright (c) 2008-2012 Wade A. Reynolds. All rights reserved.
// http://drill.codeplex.com/
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#endregion
using System;

namespace Drill.LocatorPattern
{
	/// <summary>
	/// Base locator class that can be used to create specific locator implementations.
	/// </summary>
	/// <typeparam name="T">The type to be located.</typeparam>
	public abstract class Locator<T> : ILocator<T>
		where T : class
	{
		protected abstract T LocateInstance();

		/// <summary>
		/// Locates this instance.
		/// </summary>
		/// <returns>An instance of the type T or null if the type could not be located.</returns>
		/// <exception cref="Drill.LocatorPattern.LocatorException">
		/// An instance of the type could not be located because an exception occurred. 
		/// </exception>
		public T Locate()
		{
			CheckDisposed();

			T instance;
			try
			{
				instance = LocateInstance();
			}
			catch (Exception ex)
			{
				throw new LocatorException(
					ex
					, "An instance of '{0}' could not be located because an exception occurred. "
					  + "See the inner exception for details."
					, typeof (T).FullName
					);
			}
			return instance;
		}

		#region " IDisposable Interface and Disposable Pattern Implementation "

		/// <summary>
		/// Gets a value indicating whether this instance is disposed.
		/// </summary>
		/// <value><c>true</c> if this instance is disposed; otherwise, <c>false</c>.</value>
		public bool IsDisposed { get; private set; }

		/// <summary>
		/// Performs managed and unmanaged resource cleanup.
		/// </summary>
		public void Dispose()
		{
			// NOTE: The Dispose() method is public.
			// NOTE: The Dispose() method is not virtual because it should not be overridden.

			if (false == IsDisposed)
			{
				Dispose(true);
				GC.SuppressFinalize(this);
			}
		}

		/// <summary>
		/// Performs managed and unmanaged resource cleanup.
		/// </summary>
		/// <param name="disposing"><c>true</c> if disposing, <c>false</c> if finalizing.</param>
		private void Dispose(bool disposing)
		{
			// NOTE: The Dispose(bool disposing) method is private.
			// NOTE: The Dispose(bool disposing) method is not virtual (because it is private).

			// When the operation is Dispose, call DisposeManagedResources AND DisposeUnmanagedResources.
			// When the operation is Finalize, only call DisposeUnmanagedResources.

			if (false == IsDisposed)
			{
				if (disposing)
				{
					DisposeManagedResources();
				}

				DisposeUnmanagedResources();

				// Set the _isDisposed flag.
				IsDisposed = true;
			}
		}

		protected virtual void DisposeManagedResources()
		{
			// NOTE: The DisposeManagedResources() method is protected.
			// NOTE: The DisposeManagedResources() method is virtual.

			// Clean up managed resources here.
			// * Call Dispose on all disposable object references
			// * Unsubscribe from all subscribed events
			// * Set managed object references to null
		}

		protected virtual void DisposeUnmanagedResources()
		{
			// NOTE: The DisposeUnmanagedResources() method is protected.
			// NOTE: The DisposeUnmanagedResources() method is virtual.

			// Clean up unmanaged resources here.
			// * Release handles
			// * Free allocated memory
			// * Dereference COM objects
			// * Set large fields to null
		}

		/// <summary>
		/// Determines if the instance has been disposed.
		/// </summary>
		/// <exception cref="System.ObjectDisposedException">
		/// The instance has been disposed.
		/// </exception>
		protected void CheckDisposed()
		{
			if (IsDisposed)
			{
				string objectName = GetType().Name;
				throw new ObjectDisposedException(objectName, string.Format("The {0} instance has been disposed.", objectName));
			}
		}

		#endregion " IDisposable Interface and Disposable Pattern Implementation "
	}
}